﻿/******************************************************************************
 * Copyright (C) 2015  671643387@qq.com
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by the
 * Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
 * more details.
 *
 * You should have received a copy of the GNU General Public License along
 * with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 *
 *****************************************************************************/

#include "ObjectManager.h"
#include "Shared/Typedef.h"
#include "Shared/Log/Log.h"
#include "Shared/ProgressBar.h"
#include "GlobalVars.h"
#include "SQLStorages.h"
#include "DBCStores.h"
#include "World/World.h"

// 加载角色出生点数据
bool ObjectManager::_LoadPlayreCreateInfo(void)
{
    {
        QueryResultPtr _Result(sWorldDB.PQuery("SELECT "
        "race, "          // 种族
        "class, "         // 职业
        "map, "           // 地图
        "zone, "          // 区域
        "position_x, "    // x坐标
        "position_y, "    // y坐标
        "position_z, "    // z坐标
        "orientation "    // 方位
        "FROM playercreateinfo"));
        std::uint32_t _Count = 0;

        if (!_Result)
        {
            sLog.outString();
            sLog.outString(">> Loaded %u player create definitions", _Count);
            sLog.outDbError("Error loading `playercreateinfo` table or empty table.");
            return false;
        }

        BarGoLink _Bar((int)_Result->GetRowCount());

        do
        {
            Field* _Fields = _Result->Fetch();

            std::uint32_t _CurrentRace = _Fields[0].AsUInt32();
            std::uint32_t _CurrentClass = _Fields[1].AsUInt32();
            std::uint32_t _MapId = _Fields[2].AsUInt32();
            std::uint32_t _AreaId = _Fields[3].AsUInt32();
            float  _PositionX = _Fields[4].AsFloat();
            float  _PositionY = _Fields[5].AsFloat();
            float  _PositionZ = _Fields[6].AsFloat();
            float  _Orientation = _Fields[7].AsFloat();

            ChrRacesEntry const* _RaceEntry = sDBCStores.GetChrRace(_CurrentRace);

            // TODO:数据检查

            PlayerInfo* _Info = &PlayerInfo_[_CurrentRace][_CurrentClass];
            _Info->MapId = _MapId;
            _Info->AreaId = _AreaId;
            _Info->PositionX = _PositionX;
            _Info->PositionY = _PositionY;
            _Info->PositionZ = _PositionZ;
            _Info->Orientation = _Orientation;
            _Info->DisplayId_M = _RaceEntry->Model_M;
            _Info->DisplayId_F = _RaceEntry->Model_F;

            _Bar.Step();
            ++_Count;
        } while (_Result->NextRow());

        sLog.outString();
        sLog.outString(">> Loaded %u player create definitions", _Count);
    }

    return true;
}

// 角色初始道具数据
bool ObjectManager::_LoadPlayreCreateInfoItem(void)
{
    {
        QueryResultPtr _Result(sWorldDB.PQuery("SELECT "
        "race, "    // 种族
        "class, "   // 职业
        "itemid, "  // 道具ID
        "amount "   // 道具数量
        "FROM playercreateinfo_item"));
        std::uint32_t _Count = 0;

        if (!_Result)
        {
            BarGoLink _Bar(1);
            _Bar.Step();
            sLog.outString();
            sLog.outString(">> Loaded %u custom player create items", _Count);
        }
        else
        {
            BarGoLink _Bar((int)_Result->GetRowCount());

            do
            {
                Field* _Fields = _Result->Fetch();
                std::uint32_t _CurrentRace = _Fields[0].AsUInt32();
                std::uint32_t _CurrentClass = _Fields[1].AsUInt32();
                std::uint32_t _ItemId = _Fields[2].AsUInt32();
                std::uint32_t _Amount = _Fields[3].AsUInt32();

                // 数据检查
                {
                    ChrRacesEntry const* _RaceEntry = sDBCStores.GetChrRace(_CurrentRace);
                    if (!_RaceEntry || !((1 << (_CurrentRace - 1)) & RACEMASK_ALL_PLAYABLE)) {
                        sLog.outDbError("Wrong race %u in `playercreateinfo_item` table, ignoring.",
                            _CurrentRace);
                        continue;
                    }

                    ChrClassesEntry const* _ClassEntry = sDBCStores.GetChrClass(_CurrentClass);
                    if (!_ClassEntry || !((1 << (_CurrentClass - 1)) & CLASSMASK_ALL_PLAYABLE)) {
                        sLog.outDbError("Wrong class %u in `playercreateinfo_item` table, ignoring.",
                            _CurrentClass);
                        continue;
                    }

                    if (!GetItemPrototype(_ItemId)) {
                        sLog.outDbError("Item id %u (race %u class %u) in `playercreateinfo_item` table"
                            " but not listed in `item_template`, ignoring.",
                            _ItemId, _CurrentRace, _CurrentClass);
                        continue;
                    }

                    if (!_Amount) {
                        sLog.outDbError("Item id %u (class %u race %u) have amount==0 in "
                            "`playercreateinfo_item` table, ignoring.",
                            _ItemId, _CurrentRace, _CurrentClass);
                        continue;
                    }
                }

                PlayerInfo* _Info = &PlayerInfo_[_CurrentRace][_CurrentClass];
                _Info->Item.push_back(PlayerCreateInfoItem(_ItemId, _Amount));

                _Bar.Step();
                ++_Count;
            } while (_Result->NextRow());

            sLog.outString();
            sLog.outString(">> Loaded %u custom player create items", _Count);
        }
    }

    return true;
}

// 角色初始技能
bool ObjectManager::_LoadPlayreCreateInfoSpell(void)
{
    {
        QueryResultPtr _Result(sWorldDB.PQuery("SELECT "
        "race, "    // 种族
        "class, "   // 职业
        "Spell "    // 技能
        "FROM playercreateinfo_spell"));
        std::uint32_t _Count = 0;

        if (!_Result)
        {
            BarGoLink _Bar(1);
            sLog.outString();
            sLog.outString(">> Loaded %u player create spells", _Count);
            sLog.outDbError("Error loading `playercreateinfo_spell` table or empty table.");
        }
        else
        {
            BarGoLink _Bar((int)_Result->GetRowCount());

            do
            {
                Field* _Fields = _Result->Fetch();
                std::uint32_t _CurrentRace = _Fields[0].AsUInt32();
                std::uint32_t _CurrentClass = _Fields[1].AsUInt32();
                std::uint32_t _SpellId = _Fields[2].AsUInt32();

                // 数据检查
                {
                    ChrRacesEntry const* _RaceEntry = sDBCStores.GetChrRace(_CurrentRace);
                    if (!_RaceEntry || !((1 << (_CurrentRace - 1)) & RACEMASK_ALL_PLAYABLE)) {
                        sLog.outDbError("Wrong race %u in `playercreateinfo_spell` table, ignoring.",
                            _CurrentRace);
                        continue;
                    }

                    ChrClassesEntry const* _ClassEntry = sDBCStores.GetChrClass(_CurrentClass);
                    if (!_ClassEntry || !((1 << (_CurrentClass - 1)) & CLASSMASK_ALL_PLAYABLE)) {
                        sLog.outDbError("Wrong class %u in `playercreateinfo_spell` table, ignoring.",
                            _CurrentClass);
                        continue;
                    }
                }

                PlayerInfo* _Info = &PlayerInfo_[_CurrentRace][_CurrentClass];
                _Info->Spell.push_back(_SpellId);

                _Bar.Step();
                ++_Count;
            } while (_Result->NextRow());

            sLog.outString();
            sLog.outString(">> Loaded %u player create spells", _Count);
        }
    }

    return true;
}

// 角色初始动作条
bool ObjectManager::_LoadPlayreCreateInfoAction(void)
{
    {
        QueryResultPtr _Result(sWorldDB.PQuery("SELECT "
        "race, "    // 种族
        "class, "   // 职业
        "button, "  // 动作条按钮ID
        "action, "  // 动作ID（技能编号/道具编号/宏编号）
        "type "     // 动作类型（0：技能，64：宏，128：道具）
        "FROM playercreateinfo_action"));
        std::uint32_t _Count = 0;

        if (!_Result)
        {
            BarGoLink _Bar(1);

            sLog.outString();
            sLog.outString(">> Loaded %u player create actions", _Count);
            sLog.outDbError("Error loading `playercreateinfo_action` table or empty table.");
        }
        else
        {
            BarGoLink _Bar((int)_Result->GetRowCount());

            do
            {
                Field* _Fields = _Result->Fetch();
                std::uint32_t _CurrentRace = _Fields[0].AsUInt32();
                std::uint32_t _CurrentClass = _Fields[1].AsUInt32();
                std::uint8_t _ActionButton = _Fields[2].AsUInt8();
                std::uint32_t _Action = _Fields[3].AsUInt32();
                std::uint8_t _ActionType = _Fields[4].AsUInt8();

                // 数据检查
                {
                    ChrRacesEntry const* _RaceEntry = sDBCStores.GetChrRace(_CurrentRace);
                    if (!_RaceEntry || !((1 << (_CurrentRace - 1)) & RACEMASK_ALL_PLAYABLE)) {
                        sLog.outDbError("Wrong race %u in `playercreateinfo_action` table, ignoring.",
                            _CurrentRace);
                        continue;
                    }

                    ChrClassesEntry const* _ClassEntry = sDBCStores.GetChrClass(_CurrentClass);
                    if (!_ClassEntry || !((1 << (_CurrentClass - 1)) & CLASSMASK_ALL_PLAYABLE)) {
                        sLog.outDbError("Wrong class %u in `playercreateinfo_action` table, ignoring.",
                            _CurrentClass);
                        continue;
                    }
                }

                PlayerInfo* _Info = &PlayerInfo_[_CurrentRace][_CurrentClass];
                _Info->Action.push_back(PlayerCreateInfoAction(_ActionButton, _Action, _ActionType));

                _Bar.Step();
                ++_Count;
            } while (_Result->NextRow());

            sLog.outString();
            sLog.outString(">> Loaded %u player create actions", _Count);
        }
    }

    return true;
}

// 各职业基础生命值/魔法值
bool ObjectManager::_LoadPlayerClassLevelStats(void)
{
    {
        QueryResultPtr _Result(sWorldDB.PQuery("SELECT "
        "class, "    // 职业
        "level, "    // 等级
        "basehp, "   // 生命值
        "basemana "  // 魔法值
        "FROM player_classlevelstats"));
        std::uint32_t _Count = 0;

        if (!_Result)
        {
            BarGoLink _Bar(1);
            sLog.outString();
            sLog.outString(">> Loaded %u level health/mana definitions", _Count);
            sLog.outDbError("Error loading `player_classlevelstats` table or empty table.");
            return false;
        }

        BarGoLink _Bar((int)_Result->GetRowCount());

        do
        {
            Field* _Fields = _Result->Fetch();
            std::uint32_t _CurrentClass = _Fields[0].AsUInt32();
            std::uint32_t _CurrentLevel = _Fields[1].AsUInt32();

            // 数据检查
            {
                if (_CurrentClass >= MAX_CLASSES) {
                    sLog.outDbError("Wrong class %u in `player_classlevelstats` table, ignoring.",
                        _CurrentClass);
                    continue;
                }

                if (_CurrentLevel == 0) {
                    sLog.outDbError("Wrong level %u in `player_classlevelstats` table, ignoring.",
                        _CurrentLevel);
                    continue;
                }
                else if (_CurrentLevel > sWorld.GetConfig(CONFIG_UINT32_MAX_PLAYER_LEVEL))
                {
                    if (_CurrentLevel > STRONG_MAX_LEVEL)       // hardcoded level maximum
                    {
                        sLog.outDbError("Wrong (> %u) level %u in `player_classlevelstats` table"
                            ", ignoring.", STRONG_MAX_LEVEL, _CurrentLevel);
                    }
                    else
                    {
                        sLog.outDetail("Unused (> MaxPlayerLevel in Game.conf) level %u in "
                            "`player_classlevelstats` table, ignoring.", _CurrentLevel);
                        ++_Count;                                // make result loading percent "expected" correct in case disabled detail mode for example.
                    }
                    continue;
                }
            }

            PlayerClassInfo* _ClassInfo = &PlayerClassInfo_[_CurrentClass];
            if (!_ClassInfo->LevelInfo.size()) {
                _ClassInfo->LevelInfo.resize(sWorld.GetConfig(CONFIG_UINT32_MAX_PLAYER_LEVEL));
            }

            PlayerClassLevelInfo* _ClassLevelInfo = &_ClassInfo->LevelInfo[_CurrentLevel - 1];
            _ClassLevelInfo->BaseHealth = _Fields[2].AsUInt16();
            _ClassLevelInfo->BaseMana = _Fields[3].AsUInt16();

            _Bar.Step();
            ++_Count;
        } while (_Result->NextRow());

        sLog.outString();
        sLog.outString(">> Loaded %u level health/mana definitions", _Count);
    }

    // 填充空白/检查数据
    for (int _Class = 0; _Class < MAX_CLASSES; ++_Class)
    {
        // 跳过不存在的职业
        if (!sDBCStores.GetChrClass(_Class)) {
            continue;
        }

        PlayerClassInfo* _ClassInfo = &PlayerClassInfo_[_Class];

        // 不能没有1级的数据
        if (!_ClassInfo->LevelInfo.size() || _ClassInfo->LevelInfo[0].BaseHealth == 0) {
            sLog.outDbError("Class %i Level 1 does not have health/mana data!", _Class);
            return false;
        }

        // 如果当前等级的值为0则使用低一个等级的数据
        for (std::uint32_t _Level = 1; _Level < sWorld.GetConfig(CONFIG_UINT32_MAX_PLAYER_LEVEL); ++_Level)
        {
            if (_ClassInfo->LevelInfo[_Level].BaseHealth == 0) {
                sLog.outDbError("Class %i Level %i does not have health/mana data."
                    " Using stats data of level %i.", _Class, _Level + 1, _Level);
                _ClassInfo->LevelInfo[_Level] = _ClassInfo->LevelInfo[_Level - 1];
            }
        }
    }

    return true;
}

// 角色属性
bool ObjectManager::_LoadPlayerLevelStats(void)
{
    {
        QueryResultPtr _Result(sWorldDB.PQuery("SELECT "
        "race, "     // 种族
        "class, "    // 职业
        "level, "    // 等级
        "str, "      // 力量
        "agi, "      // 敏捷
        "sta, "      // 耐力
        "inte, "     // 智力
        "spi "       // 精神
        "FROM player_levelstats"));
        std::uint32_t _Count = 0;

        if (!_Result.get())
        {
            BarGoLink _Bar(1);

            sLog.outString();
            sLog.outString(">> Loaded %u level stats definitions", _Count);
            sLog.outDbError("Error loading `player_levelstats` table or empty table.");
            return false;
        }

        BarGoLink _Bar((int)_Result->GetRowCount());

        do
        {
            Field* _Fields = _Result->Fetch();

            std::uint32_t _CurrentRace = _Fields[0].AsUInt32();
            std::uint32_t _CurrentClass = _Fields[1].AsUInt32();
            std::uint32_t _CurrentLevel = _Fields[2].AsUInt32();

            // 数据检查
            {
                ChrRacesEntry const* _RaceEntry = sDBCStores.GetChrRace(_CurrentRace);
                if (!_RaceEntry || !((1 << (_CurrentRace - 1)) & RACEMASK_ALL_PLAYABLE)) {
                    sLog.outDbError("Wrong race %u in `player_levelstats` table, ignoring.", _CurrentRace);
                    continue;
                }

                ChrClassesEntry const* _ClassEntry = sDBCStores.GetChrClass(_CurrentClass);
                if (!_ClassEntry || !((1 << (_CurrentClass - 1)) & CLASSMASK_ALL_PLAYABLE)) {
                    sLog.outDbError("Wrong class %u in `player_levelstats` table, ignoring.", _CurrentClass);
                    continue;
                }

                if (_CurrentLevel > sWorld.GetConfig(CONFIG_UINT32_MAX_PLAYER_LEVEL))
                {
                    if (_CurrentLevel > STRONG_MAX_LEVEL) {
                        sLog.outDbError("Wrong (> %u) level %u in `player_levelstats` table,"
                            " ignoring.", STRONG_MAX_LEVEL, _CurrentLevel);
                    }
                    else {
                        sLog.outDetail("Unused (> MaxPlayerLevel in Game.conf) level %u in "
                            "`player_levelstats` table, ignoring.", _CurrentLevel);
                        ++_Count;
                    }
                    continue;
                }
            }

            PlayerInfo* _Info = &PlayerInfo_[_CurrentRace][_CurrentClass];
            if (!_Info->LevelInfo.size()) {
                _Info->LevelInfo.resize(sWorld.GetConfig(CONFIG_UINT32_MAX_PLAYER_LEVEL));
            }

            PlayerLevelInfo* _LevelInfo = &_Info->LevelInfo[_CurrentLevel - 1];
            for (int _I = 0; _I < MAX_STATS; ++_I) {
                _LevelInfo->Stats[_I] = _Fields[_I + 3].AsUInt8();
            }

            _Bar.Step();
            ++_Count;
        } while (_Result->NextRow());

        sLog.outString();
        sLog.outString(">> Loaded %u level stats definitions", _Count);
    }

    // 填充空白/检查数据
    for (int _Race = 0; _Race < MAX_RACES; ++_Race)
    {
        // 跳过不存在的种族
        if (!((1 << (_Race - 1)) & RACEMASK_ALL_PLAYABLE) || !sDBCStores.GetChrRace(_Race)) {
            continue;
        }

        for (int _Class = 0; _Class < MAX_CLASSES; ++_Class)
        {
            // 跳过不存在的职业
            if (!((1 << (_Class - 1)) & CLASSMASK_ALL_PLAYABLE) || !sDBCStores.GetChrClass(_Class))
            {
                continue;
            }

            PlayerInfo* _Info = &PlayerInfo_[_Race][_Class];

            // 跳过没有模型的职业
            if (!_Info->DisplayId_M || !_Info->DisplayId_F) {
                continue;
            }

            // 不能没有1级的数据
            if (!_Info->LevelInfo.size() || _Info->LevelInfo[0].Stats[0] == 0)
            {
                sLog.outDbError("Race %i Class %i Level 1 does not have stats data!", _Race, _Class);
                return false;
            }

            // 填充等级空白，使用上一个等级的数据
            for (std::uint32_t _Level = 1; _Level < sWorld.GetConfig(CONFIG_UINT32_MAX_PLAYER_LEVEL); ++_Level)
            {
                if (_Info->LevelInfo[_Level].Stats[0] == 0)
                {
                    sLog.outDbError("Race %i Class %i Level %i does not have stats data. "
                        "Using stats data of level %i.", _Race, _Class, _Level + 1, _Level);
                    _Info->LevelInfo[_Level] = _Info->LevelInfo[_Level - 1];
                }
            }
        }
    }

    return true;
}

// 各等级经验
bool ObjectManager::_LoadPlayerXpForLevel(void)
{
    {
        PlayerXPperLevel_.resize(sWorld.GetConfig(CONFIG_UINT32_MAX_PLAYER_LEVEL));
        for (std::uint32_t _Level = 0; _Level < sWorld.GetConfig(CONFIG_UINT32_MAX_PLAYER_LEVEL); ++_Level) {
            PlayerXPperLevel_[_Level] = 0;
        }

        QueryResultPtr _Result(sWorldDB.PQuery("SELECT "
            "lvl, "               // 等级
            "xp_for_next_level "  // 升到下一级需要的经验
            "FROM player_xp_for_level"));
        std::uint32_t _Count = 0;

        if (!_Result)
        {
            BarGoLink _Bar(1);
            sLog.outString();
            sLog.outString(">> Loaded %u xp for level definitions", _Count);
            sLog.outDbError("Error loading `player_xp_for_level` table or empty table.");
            return false;
        }

        BarGoLink _Bar((int)_Result->GetRowCount());

        do
        {
            Field* _Fields = _Result->Fetch();
            std::uint32_t _CurrentLevel = _Fields[0].AsUInt32();
            std::uint32_t _CurrentXp = _Fields[1].AsUInt32();

            // 数据检查
            {
                if (_CurrentLevel >= sWorld.GetConfig(CONFIG_UINT32_MAX_PLAYER_LEVEL))
                {
                    if (_CurrentLevel > STRONG_MAX_LEVEL)
                    {
                        sLog.outDbError("Wrong (> %u) level %u in `player_xp_for_level` "
                            "table, ignoring.", STRONG_MAX_LEVEL, _CurrentLevel);
                    }
                    else
                    {
                        sLog.outDetail("Unused (> MaxPlayerLevel in Game.conf) level "
                            "%u in `player_xp_for_levels` table, ignoring.", _CurrentLevel);
                        ++_Count;
                    }
                    continue;
                }
            }

            PlayerXPperLevel_[_CurrentLevel] = _CurrentXp;
            _Bar.Step();
            ++_Count;
        } while (_Result->NextRow());

        sLog.outString();
        sLog.outString(">> Loaded %u xp for level definitions", _Count);
    }

    // 填充空白
    for (std::uint32_t _Level = 1; _Level < sWorld.GetConfig(CONFIG_UINT32_MAX_PLAYER_LEVEL); ++_Level)
    {
        if (PlayerXPperLevel_[_Level] == 0)
        {
            sLog.outDbError("Level %i does not have XP for level data. "
                "Using data of level [%i] + 100.", _Level + 1, _Level);
            PlayerXPperLevel_[_Level] = PlayerXPperLevel_[_Level - 1] + 100;
        }
    }

    return true;
}

// 角色名，角色ID映射
bool ObjectManager::_LoadPlayerNames(void)
{
    QueryResultPtr _Result(sCharaDB.PQuery("SELECT guid, name FROM characters;"));
    if (_Result) {
        do 
        {
            Name2Guid_[(*_Result)[1].AsString()] = (*_Result)[0].AsUInt32();
        } while (_Result->NextRow());
    }
	sLog.outString(">> Loaded %u character names", Name2Guid_.size());
    return true;
}

bool ObjectManager::LoadPlayerInfo(void)
{
    bool _LoadOk = _LoadPlayreCreateInfo();
    _LoadOk &= _LoadPlayreCreateInfoItem();
    _LoadOk &= _LoadPlayreCreateInfoSpell();
    _LoadOk &= _LoadPlayreCreateInfoAction();
    _LoadOk &= _LoadPlayerClassLevelStats();
    _LoadOk &= _LoadPlayerLevelStats();
    _LoadOk &= _LoadPlayerXpForLevel();
    _LoadOk &= _LoadPlayerNames();
    return _LoadOk;
}